﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Noris.Schedule.Support.Sql
{
	#region SqlParseCommandSelect : SQL příkaz SELECT
	/// <summary>
	/// SqlParseCommandSelect : SQL příkaz SELECT
	/// </summary>
	public class SqlParseCommandSelect : SqlParseCommand, ISqlCmdWithFrom
	{
		#region INSTANČNÍ METODY PRO PRÁCI SE SELECTEM
		/// <summary>
		/// Souhrn všech klíčových slov, které tento objekt dokáže obsluhovat.
		/// Tento List se čte typicky jen jednou, při iniciaci systému.
		/// Klíčová slova mají být Trim().ToUpper().
		/// </summary>
		public override IEnumerable<string> KeyWordList { get { return new string[] { "SELECT" }; } }
		/// <summary>
		/// Klíčové slovo tohoto příkazu (SELECT, UPDATE, INSERT, DELETE, CREATE, ALTER, ...)
		/// </summary>
		public override string SqlCommand { get { return "SELECT"; } }
		/// <summary>
		/// Zde je třeba nadeklarovat položky syntaxe
		/// </summary>
		/// <param name="sqlQueue"></param>
		protected override void SyntaxDeclare(Queue<SqlParseItem> sqlQueue)
		{
			this.SyntaxAdd("SELECT", true);
			this.SyntaxAdd("INTO", false);
			this.SyntaxAdd("FROM", true);
			this.SyntaxAdd("WHERE", false);
			this.SyntaxAdd("GROUP", false);
			this.SyntaxAdd("HAVING", false);
			this.SyntaxAdd("ORDER", false);
			this.PhraseFrom = null;
		}
		/// <summary>
		/// Zajistí obalení aktuálního obsahu dané syntaktické fráze tak, aby na její konec mohl být přidán další výraz.
		/// SQL příkaz je this, dotyčná syntaktická fráze je v parametru syntaxItem, 
		/// přidávaný výraz je již parsován v poli v parametru sqlItems.
		/// Metoda není volána, pokud dotyčná syntaktická fráze (parametr syntaxItem) je dosud prázdný ani když je prázdný přidávaný výraz.
		/// Wrapování může pro snadné oddělení využít metod instanci SqlParseSyntaxItem: WrapWithComma(), WrapWithSpace(), WrapWithParenthesize()
		/// Anebo lze využít metody nižší úrovně: ParenthesiseContent(), AddContentOnBegin(), AddContentOnEnd()
		/// Pokud metoda chce obalit stávající content závorkami, je vhodné vyžít metodu SqlParseSyntaxItem.ParenthesiseContent().
		/// </summary>
		/// <param name="syntaxItem">Syntaktická položka</param>
		/// <param name="sqlItems">Seznam textových položek, které se budou přidávat (je možno nakouknout a přizpůsobit se).</param>
		public override void WrapSyntaxContent(SqlParseSyntaxItem syntaxItem, List<SqlParseItem> sqlItems)
		{
			switch (syntaxItem.KeyWord)
			{
				case "SELECT":
					syntaxItem.WrapWithComma();
					break;
				case "INTO":
					syntaxItem.WrapWithComma();
					break;
				case "FROM":
					syntaxItem.WrapWithSpace();
					break;
				case "WHERE":
					bool isContent = syntaxItem.IsContentSignificant;
					syntaxItem.WrapWithParenthesize(" AND ");        // Ozávorkovat současný Content a přidat spojku " AND "
					if (isContent)                                   // Pokud současný Content je významný (není prázdný):
						SqlParseCommand.ParenthesiseItems(sqlItems); // Ozávorkovat !!! přidávaný text (pokud to není zbytečné)
					break;
				case "GROUP":
					syntaxItem.WrapWithComma();
					break;
				case "HAVING":
					syntaxItem.WrapWithParenthesize("AND");
					break;
				case "ORDER":
					syntaxItem.WrapWithComma();
					break;
			}
		}
		/// <summary>
		/// Háček pro potomka, když je SQL command načten.
		/// Zde: z položek fráze FROM se odvodí objekt this.FromTable, který poskytuje exaktní přístup k jednotlivým tabulkám.
		/// </summary>
		protected override void LoadCommandAfter()
		{
			// Vytvořím frázi FROM (parsování tabulek, aliasů, joinů):
			SqlParseSyntaxItem syntax = this.SyntaxFind("FROM");
			this.PhraseFrom = SqlParsePhraseFrom.CreateFromQueue(syntax);

			// Převedu některá pomocná klíčová slova z Content do Keyword:
			this.SyntaxModify("SELECT", "ALL", "DISTINCT", "TOP *", "TOP * PERCENT", "TOP * %", "WITH TIES");
			this.SyntaxModify("ORDER", "BY");
			this.SyntaxModify("GROUP", "BY");
		}
		#endregion
		#region IMPLEMENTACE ISqlCmdWithFrom : property PhraseFrom pro přímou práci s obsahem fráze FROM
		/// <summary>
		/// Parsovaná fráze FROM
		/// </summary>
		public SqlParsePhraseFrom PhraseFrom { get; private set; }
		#endregion
		#region STATICKÉ METODY PRO APLIKACI VÝRAZU DO SQL SELECTU
		/// <summary>
		/// Do předaného SQL selectu přidá do specifikované fráze (parametr syntax) daný text (výraz, expression).
		/// </summary>
		/// <param name="sqlText">Inp/Out syntaxe SQL příkazu</param>
		/// <param name="command">SQL příkaz, kterého se zásah týká. Typicky "SELECT".</param>
		/// <param name="syntax">Syntaktická fráze, kam se má výraz vložit. Například "WHERE"</param>
		/// <param name="expression">Výraz k přidání</param>
		public static void AddExpression(ref string sqlText, string command, string syntax, string expression)
		{
			using (SqlText parse = AddExpression(sqlText, command, syntax, expression))
			{
				// V objektu parse je aplikován výraz, takže jen načteme zpátky výsledný text:
				sqlText = parse.OutputSqlText;
			}
		}
		/// <summary>
		/// Do předaného SQL selectu přidá do specifikované fráze (parametr syntax) daný text (výraz, expression).
		/// </summary>
		/// <param name="sqlText">Inp/Out syntaxe SQL příkazu</param>
		/// <param name="command">SQL příkaz, kterého se zásah týká. Typicky "SELECT".</param>
		/// <param name="syntax">Syntaktická fráze, kam se má výraz vložit. Například "WHERE"</param>
		/// <param name="expression">Výraz k přidání</param>
		public static SqlText AddExpression(string sqlText, string command, string syntax, string expression)
		{
			SqlText parse = SqlText.CreateFromText(sqlText);

			SqlParseCommand sqlCommand = parse.FindFirstCommand(command);
			if (sqlCommand == null)
				throw new ArgumentException("SQL command «" + command + "» does not exists in given text.", "sqlText");

			// Aplikuje se výraz do požadované syntaxe, a tím i do textu v parse:
			sqlCommand.AddSyntaxExpression(syntax, expression, true);

			return parse;
		}
		/// <summary>
		/// Do předaného SQL selectu přidá do specifikované fráze (parametr syntax) daný text (výraz, expression).
		/// </summary>
		/// <param name="sqlText">Inp/Out syntaxe SQL příkazu</param>
		/// <param name="command">SQL příkaz, kterého se zásah týká. Typicky "SELECT".</param>
		/// <param name="syntax">Syntaktická fráze, kam se má přidat Keywords. Například "SELECT"</param>
		/// <param name="keywords">Klíčová slova k přidání. Například "DISTINCT".</param>
		/// <param name="addAfterWords">Seznam klíčových slov, za která (za první nalezené) se nové klíčové slovo přidá. Například "SELECT".</param>
		public static void AddKeyword(ref string sqlText, string command, string syntax, string keywords, params string[] addAfterWords)
		{
			using (SqlText parse = _AddKeyword(sqlText, command, syntax, keywords, addAfterWords))
			{
				// V objektu parse je aplikován výraz, takže jen načteme zpátky výsledný text:
				sqlText = parse.OutputSqlText;
			}
		}
		/// <summary>
		/// Do předaného SQL selectu přidá do specifikované fráze (parametr syntax) daný text (výraz, expression).
		/// </summary>
		/// <param name="sqlText">Inp/Out syntaxe SQL příkazu</param>
		/// <param name="command">SQL příkaz, kterého se zásah týká. Typicky "SELECT".</param>
		/// <param name="syntax">Syntaktická fráze, kam se má výraz vložit</param>
		/// <param name="keywords">Klíčová slova k přidání. Například "DISTINCT".</param>
		/// <param name="addAfterWords">Seznam klíčových slov, za která (za první nalezené) se nové klíčové slovo přidá. Například "SELECT".</param>
		public static SqlText AddKeyword(string sqlText, string command, string syntax, string keywords, params string[] addAfterWords)
		{
			return _AddKeyword(sqlText, command, syntax, keywords, addAfterWords);
		}
		/// <summary>
		/// Do předaného SQL selectu přidá do specifikované fráze (parametr syntax) daný text (výraz, expression).
		/// </summary>
		/// <param name="sqlText">Inp/Out syntaxe SQL příkazu</param>
		/// <param name="command">SQL příkaz, kterého se zásah týká. Typicky "SELECT".</param>
		/// <param name="syntax">Syntaktická fráze, kam se má výraz vložit</param>
		/// <param name="keywords">Klíčová slova k přidání. Například "DISTINCT".</param>
		/// <param name="addAfterWords">Seznam klíčových slov, za která (za první nalezené) se nové klíčové slovo přidá. Například "SELECT".</param>
		private static SqlText _AddKeyword(string sqlText, string command, string syntax, string keywords, IEnumerable<string> addAfterWords)
		{
			SqlText parse = SqlText.CreateFromText(sqlText);

			SqlParseCommand sqlCommand = parse.FindFirstCommand(command);
			if (sqlCommand == null)
				throw new ArgumentException("SQL command «" + command + "» does not exists in given text.", "sqlText");

			// Aplikuje se výraz do požadované syntaxe, a tím i do textu v parse:
			sqlCommand.AddKeyWord(syntax, keywords, addAfterWords);

			return parse;
		}
		#endregion
	}
	#endregion
}
